/*
 * JFugue - API for Music Programming
 * Copyright (C) 2003-2008  David Koelle
 *
 * http://www.jfugue.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

package org.jfugue;

/**
 * Contains all information necessary for a musical note, including pitch,
 * duration, attack velocity, and decay velocity.
 *
 * <p>
 * Most of these settings have defaults. The default octave is 5. The default
 * duration is a quarter note. The default attack and decay velocities are 64.
 * </p>
 *
 * @author David Koelle
 * @version 2.0.1
 */
public final class Note implements JFugueElement {
	private byte value = 0;
	private long duration = 0;
	private double decimalDuration = 0.0;
	private boolean isStartOfTie = false;
	private boolean isEndOfTie = false;
	private byte attackVelocity = DEFAULT_VELOCITY;
	private byte decayVelocity = DEFAULT_VELOCITY;
	private boolean rest = false;
	private byte type = 0;
	private boolean accompanyingNotes = false;

	/**
	 * Instantiates a new Note object.
	 */
	public Note() {
		this.value = 0;
		this.duration = 0;
		this.type = 0;
	}

	/**
	 * Instantiates a new Note object with the given note value. This
	 * constructor should only be called in cases where the duration of the note
	 * is not important (for example, when specifying a root note for a
	 * IntervalNotation)
	 * 
	 * @param value
	 *            the numeric value of the note. C5 is 60.
	 */
	public Note(byte value) {
		this(value, 1);
	}

	/**
	 * Instantiates a new Note object with the given note value and duration.
	 * 
	 * @param value
	 *            the numeric value of the note. C5 is 60.
	 * @param duration
	 *            the duration of the note, as milliseconds.
	 */
	public Note(byte value, long duration) {
		this.value = value;
		this.duration = duration;
	}

	/**
	 * Instantiates a new Note object with the given note value and duration.
	 * 
	 * @param value
	 *            the numeric value of the note. C5 is 60.
	 * @param decimalDuration
	 *            the duration of the note, as a decimal fraction of a whole
	 *            note.
	 */
	public Note(byte value, double decimalDuration) {
		this.value = value;
		this.decimalDuration = decimalDuration;
	}

	/**
	 * Instantiates a new Note object with the given note value, duration, and
	 * attack and decay velocities.
	 * 
	 * @param value
	 *            the numeric value of the note. C5 is 60.
	 * @param duration
	 *            the duration of the note.
	 */
	public Note(byte value, long duration, byte attackVelocity,
			byte decayVelocity) {
		this.value = value;
		this.duration = duration;
		this.attackVelocity = attackVelocity;
		this.decayVelocity = decayVelocity;
	}

	/**
	 * Instantiates a new Note object with the given note value, duration, and
	 * attack and decay velocities.
	 * 
	 * @param value
	 *            the numeric value of the note. C5 is 60.
	 * @param decimalDuration
	 *            the duration of the note.
	 */
	public Note(byte value, double decimalDuration, byte attackVelocity,
			byte decayVelocity) {
		this.value = value;
		this.decimalDuration = decimalDuration;
		this.attackVelocity = attackVelocity;
		this.decayVelocity = decayVelocity;
	}

	/**
	 * Indicates whether this Note object actually represents a rest.
	 * 
	 * @param rest
	 *            indicates whether this note is rest
	 */
	public void setRest(boolean rest) {
		this.rest = rest;
	}

	/**
	 * Returns whether this Note object actually represents a rest.
	 * 
	 * @return whether this note is a rest
	 */
	public boolean isRest() {
		return this.rest;
	}

	/**
	 * Sets the numeric value of this note. C5 is 60.
	 * 
	 * @param value
	 *            the value of the note
	 */
	public void setValue(byte value) {
		this.value = value;
	}

	/**
	 * Returns the numeric value of this note. C5 is 60.
	 * 
	 * @return the value of this note
	 */
	public byte getValue() {
		return this.value;
	}

	/**
	 * Sets the duration of this note.
	 * 
	 * @param duration
	 *            the duration of this note
	 */
	public void setDuration(long duration) {
		this.duration = duration;
	}

	/**
	 * Returns the duration of this note.
	 * 
	 * @return the duration of this note
	 */
	public long getDuration() {
		return this.duration;
	}

	/**
	 * Sets the decimal fraction value for the duration.
	 * 
	 * @param duration
	 *            the decimal fraction for the duration
	 */
	public void setDecimalDuration(double duration) {
		this.decimalDuration = duration;
	}

	/**
	 * Returns the decimal fraction value for the duration.
	 * 
	 * @return the decimal fraction value for the duration
	 */
	public double getDecimalDuration() {
		return this.decimalDuration;
	}

	/**
	 * Indicates whether this note has a tie to some future note.
	 * 
	 * @param startOfTie
	 *            true if the note is tied, false if not
	 */
	public void setStartOfTie(boolean startOfTie) {
		this.isStartOfTie = startOfTie;
	}

	/**
	 * Returns whether this note has a tie to some future note.
	 * 
	 * @return true is the note is tied, false if not
	 */
	public boolean isStartOfTie() {
		return this.isStartOfTie;
	}

	/**
	 * Indicates whether this note is tied to some past note.
	 * 
	 * @param endOfTie
	 *            true if the note is tied, false if not
	 */
	public void setEndOfTie(boolean endOfTie) {
		this.isEndOfTie = endOfTie;
	}

	/**
	 * Returns whether this note is tied to some past note.
	 * 
	 * @return true is the note is tied, false if not
	 */
	public boolean isEndOfTie() {
		return this.isEndOfTie;
	}

	/**
	 * Sets the attack velocity for this note.
	 * 
	 * @param velocity
	 *            the attack velocity
	 */
	public void setAttackVelocity(byte velocity) {
		this.attackVelocity = velocity;
	}

	/**
	 * Returns the attack velocity for this note.
	 * 
	 * @return the attack velocity
	 */
	public byte getAttackVelocity() {
		return this.attackVelocity;
	}

	/**
	 * Sets the decay velocity for this note.
	 * 
	 * @param velocity
	 *            the decay velocity
	 */
	public void setDecayVelocity(byte velocity) {
		this.decayVelocity = velocity;
	}

	/**
	 * Returns the decay velocity for this note.
	 * 
	 * @return the decay velocity
	 */
	public byte getDecayVelocity() {
		return this.decayVelocity;
	}

	/**
	 * Sets whether this Note will have other Notes (sequential or parallel)
	 * associated with it.
	 * 
	 * @param accompanying
	 */
	public void setHasAccompanyingNotes(boolean accompanying) {
		this.accompanyingNotes = accompanying;
	}

	/**
	 * Returns whether this Note will have other Notes (sequential or parallel)
	 * associated with it.
	 */
	public boolean hasAccompanyingNotes() {
		return this.accompanyingNotes;
	}

	/**
	 * Sets the note type - either First, Sequential, or Parallel.
	 * 
	 * @param type
	 *            the note type
	 */
	public void setType(byte type) {
		this.type = type;
	}

	/**
	 * Returns the note type - either First, Sequential, or Parallel.
	 * 
	 * @return the note type
	 */
	public byte getType() {
		return this.type;
	}

	/** Indicates that this note is the first note in the token. */
	public static final byte FIRST = 0;
	/**
	 * Indicates that this note immediately follows a previous note in the same
	 * token.
	 */
	public static final byte SEQUENTIAL = 1;
	/**
	 * Indicates that this note is played at the same time as a previous note in
	 * the same token.
	 */
	public static final byte PARALLEL = 2;
	/** Default value for attack and decay velocity. */
	public static final byte DEFAULT_VELOCITY = 64;

	/**
	 * Returns the Music String representing this element and all of its
	 * settings. For a Note object, the Music String is a note, expressed as
	 * either a letter, <code>note</code>, or a bracketed number,
	 * <code>[<i>note-value</i>]</code>,
	 * and a duration, expressed as either a letter, <code>duration</code>, or a
	 * slash followed by a numeric duration,
	 * <code>/<i>decimal-duration</i></code>
	 * <p>If either the attack or decay velocity is set to a value besides the
	 * default, <code>a<i>velocity</i></code> and/or
	 * <code>d<i>velocity</i></code> will be added to the string. If this note
	 * is to be played in sequence or in parallel to another note, a
	 * <code>+</code> or <code>_</code> character will be added as appropriate.</p>
	 * 
	 * @return the Music String for this element
	 */
	@Override
	public String getMusicString() {
		StringBuffer buffy = new StringBuffer();

		// If this is a Sequential note or a Parallel note, include that
		// information.
		if (SEQUENTIAL == this.type) {
			buffy.append("_");
		} else if (PARALLEL == this.type) {
			buffy.append("+");
		}

		// Add the note value and duration value
		// buffy.append("[");
		// buffy.append(this.value);
		// buffy.append("]/");
		// buffy.append(this.decimalDuration);
		buffy.append(Note.getStringForNote(this.value, this.decimalDuration));

		if (this.attackVelocity != DEFAULT_VELOCITY) {
			buffy.append("a");
			buffy.append(this.attackVelocity);
		}
		if (this.decayVelocity != DEFAULT_VELOCITY) {
			buffy.append("d");
			buffy.append(this.decayVelocity);
		}

		return buffy.toString();
	}

	/**
	 * Returns verification string in this format: Note: value={#},
	 * duration={#}, startTie={T|F}, endTie={T|F}, attack={#}, decay={#},
	 * isFirst={T|F}, isParallel={T|F}, isSequential={T|F}
	 * 
	 * @version 4.0
	 */
	@Override
	public String getVerifyString() {
		StringBuffer buffy = new StringBuffer();
		buffy.append("Note: value=");
		buffy.append(getValue());
		buffy.append(", duration=");
		buffy.append(getDecimalDuration());
		buffy.append(", startTie=");
		buffy.append(isStartOfTie() ? "T" : "F");
		buffy.append(", endTie=");
		buffy.append(isEndOfTie() ? "T" : "F");
		buffy.append(", attack=");
		buffy.append(getAttackVelocity());
		buffy.append(", decay=");
		buffy.append(getDecayVelocity());
		buffy.append(", isFirst=");
		buffy.append(getType() == FIRST ? "T" : "F");
		buffy.append(", isParallel=");
		buffy.append(getType() == PARALLEL ? "T" : "F");
		buffy.append(", isSequential=");
		buffy.append(getType() == SEQUENTIAL ? "T" : "F");

		return buffy.toString();
	}

	public static String createVerifyString(int value, double duration) {
		return createVerifyString(value, duration, false, false, (byte) 64,
				(byte) 64, true, false, false);
	}

	public static String createVerifyString(int value, double duration,
			boolean startTie, boolean endTie) {
		return createVerifyString(value, duration, startTie, endTie, (byte) 64,
				(byte) 64, true, false, false);
	}

	public static String createVerifyString(int value, double duration,
			int attack, int decay) {
		return createVerifyString(value, duration, false, false, attack, decay,
				true, false, false);
	}

	public static String createVerifyString(int value, double duration,
			boolean isFirst, boolean isParallel, boolean isSequential) {
		return createVerifyString(value, duration, false, false, (byte) 64,
				(byte) 64, isFirst, isParallel, isSequential);
	}

	public static String createVerifyString(int value, double duration,
			boolean startTie, boolean endTie, int attack, int decay,
			boolean isFirst, boolean isParallel, boolean isSequential) {
		StringBuffer buffy = new StringBuffer();
		buffy.append("Note: value=");
		buffy.append(value);
		buffy.append(", duration=");
		buffy.append(duration);
		buffy.append(", startTie=");
		buffy.append(startTie ? "T" : "F");
		buffy.append(", endTie=");
		buffy.append(endTie ? "T" : "F");
		buffy.append(", attack=");
		buffy.append(attack);
		buffy.append(", decay=");
		buffy.append(decay);
		buffy.append(", isFirst=");
		buffy.append(isFirst ? "T" : "F");
		buffy.append(", isParallel=");
		buffy.append(isParallel ? "T" : "F");
		buffy.append(", isSequential=");
		buffy.append(isSequential ? "T" : "F");

		return buffy.toString();
	}

	/**
	 * Easily create compound note verification strings, like: Note: value=60,
	 * duration=1.0, startTie=F, endTie=F, attack=64, decay=64, isFirst=T,
	 * isParallel=F, isSequential=F; Note: value=63, duration=0.0, startTie=F,
	 * endTie=F, attack=64, decay=64, isFirst=F, isParallel=T, isSequential=F
	 */
	public static String createCompoundVerifyString(String... strings) {
		StringBuffer buffy = new StringBuffer();

		for (String string : strings) {
			buffy.append(string);
			buffy.append("; ");
		}

		return buffy.toString().substring(0, buffy.toString().length() - 2);
	}

	/**
	 * Returns a MusicString representation of the given MIDI note value and
	 * duration -- which indicates a note and an octave.
	 * 
	 * @param noteValue
	 *            this MIDI note value, like 60
	 * @param decimalDuration
	 *            the duration of this note, like 0.5
	 * @return a MusicString value, like C5h
	 */
	public static String getStringForNote(byte noteValue,
			double decimalDuration) {
		StringBuffer buffy = new StringBuffer();
		buffy.append(getStringForNote(noteValue));
		buffy.append(getStringForDuration(decimalDuration));
		return buffy.toString();
	}

	/**
	 * Returns the frequency, in Hertz, for the given note value. For example,
	 * the frequency for A5 (MIDI note 69) is 440.0
	 * 
	 * @param noteValue
	 * @return
	 */
	public static double getFrequencyForNote(int noteValue) {
		double freq = 0.0;
		double freq0 = 8.1757989156;
		for (double d = 0; d <= noteValue; d++) {
			freq = freq0 * Math.pow(2.0, d / 12.0);
		}

		// Truncate to 4 significant digits
		double retVal = Math.rint(freq * 10000.0) / 10000.0;

		return retVal;
	}

	/**
	 * Returns a MusicString representation of the given MIDI note value --
	 * which indicates a note and an octave.
	 * 
	 * @param noteValue
	 *            this MIDI note value, like 60
	 * @return a MusicString value, like C5
	 */
	public static String getStringForNote(byte noteValue) {
		int note = noteValue % 12;
		int octave = noteValue / 12;
		StringBuffer buffy = new StringBuffer();
		buffy.append(NOTES[note]);
		buffy.append(octave);
		return buffy.toString();
	}

	/**
	 * Returns a MusicString representation of a decimal duration. This code
	 * currently only converts single duration values representing whole, half,
	 * quarter, eighth, etc. durations; and dotted durations associated with
	 * those durations (such as "h.", equal to 0.75). This method does not
	 * convert combined durations (for example, "hi" for 0.625) or anything
	 * greater than a duration of 1.0 (for example, "wwww" for 4.0). For these
	 * values, the original decimal duration is returned in a string, prepended
	 * with a "/" to make the returned value a valid MusicString duration
	 * indicator.
	 *
	 * @param decimalDuration
	 *            The decimal value of the duration to convert
	 * @return a MusicString fragment representing the duration
	 */
	public static String getStringForDuration(double decimalDuration) {
		if (decimalDuration == 1.0) {
			return "w";
		} else if (decimalDuration == 0.75) {
			return "h.";
		} else if (decimalDuration == 0.5) {
			return "h";
		} else if (decimalDuration == 0.375) {
			return "q.";
		} else if (decimalDuration == 0.25) {
			return "q";
		} else if (decimalDuration == 0.1875) {
			return "i.";
		} else if (decimalDuration == 0.125) {
			return "i";
		} else if (decimalDuration == 0.09375) {
			return "s.";
		} else if (decimalDuration == 0.0625) {
			return "s";
		} else if (decimalDuration == 0.046875) {
			return "t.";
		} else if (decimalDuration == 0.03125) {
			return "t";
		} else if (decimalDuration == 0.0234375) {
			return "x.";
		} else if (decimalDuration == 0.015625) {
			return "x";
		} else if (decimalDuration == 0.01171875) {
			return "o.";
		} else if (decimalDuration == 0.0078125) {
			return "o";
		} else {
			return "/" + decimalDuration;
		}
	}

	/**
	 * Returns the decimal duration that is equal to the given MusicString
	 * representation. This code currently only converts single duration values
	 * representing whole, half, quarter, eighth, etc. durations; and dotted
	 * durations associated with those durations (such as "h.", equal to 0.75).
	 * This method does not convert combined durations (for example, "hi" for
	 * 0.625) or anything greater than a duration of 1.0 (for example, "wwww"
	 * for 4.0). For these values, the original decimal duration is returned in
	 * a string, prepended with a "/" to make the returned value a valid
	 * MusicString duration indicator.
	 *
	 * @param stringDuration
	 *            The MusicString duration character (or dotted character)
	 * @return a decimal value representing the duration, expressed as a
	 *         fraction of a whole note
	 */
	public static double getDecimalForDuration(String stringDuration) {
		String stringDuration2 = stringDuration.toLowerCase();
		if (stringDuration2.equals("w")) {
			return 1.0;
		} else if (stringDuration2.equals("h.")) {
			return 0.75;
		} else if (stringDuration2.equals("h")) {
			return 0.5;
		} else if (stringDuration2.equals("q.")) {
			return 0.375;
		} else if (stringDuration2.equals("q")) {
			return 0.25;
		} else if (stringDuration2.equals("i.")) {
			return 0.1875;
		} else if (stringDuration2.equals("i")) {
			return 0.125;
		} else if (stringDuration2.equals("s.")) {
			return 0.09375;
		} else if (stringDuration2.equals("s")) {
			return 0.0625;
		} else if (stringDuration2.equals("t.")) {
			return 0.046875;
		} else if (stringDuration2.equals("t")) {
			return 0.03125;
		} else if (stringDuration2.equals("x.")) {
			return 0.0234375;
		} else if (stringDuration2.equals("x")) {
			return 0.015625;
		} else if (stringDuration2.equals("o.")) {
			return 0.01171875;
		} else if (stringDuration2.equals("o")) {
			return 0.0078125;
		} else {
			return 0.0;
		}
	}

	public static final String[] NOTES = new String[] { "C", "C#", "D", "Eb",
			"E", "F", "F#", "G", "G#", "A", "Bb", "B" };

	public static final byte ACOUSTIC_BASS_DRUM = 35;
	public static final byte BASS_DRUM = 36;
	public static final byte SIDE_STICK = 37;
	public static final byte ACOUSTIC_SNARE = 38;
	public static final byte HAND_CLAP = 39;

	public static final byte ELECTRIC_SNARE = 40;
	public static final byte LOW_FLOOR_TOM = 41;
	public static final byte CLOSED_HI_HAT = 42;
	public static final byte HIGH_FLOOR_TOM = 43;
	public static final byte PEDAL_HI_HAT = 44;
	public static final byte LOW_TOM = 45;
	public static final byte OPEN_HI_HAT = 46;
	public static final byte LOW_MID_TOM = 47;
	public static final byte HI_MID_TOM = 48;
	public static final byte CRASH_CYMBAL_1 = 49;

	public static final byte HIGH_TOM = 50;
	public static final byte RIDE_CYMBAL_1 = 51;
	public static final byte CHINESE_CYMBAL = 52;
	public static final byte RIDE_BELL = 53;
	public static final byte TAMBOURINE = 54;
	public static final byte SPLASH_CYMBAL = 55;
	public static final byte COWBELL = 56;
	public static final byte CRASH_CYMBAL_2 = 57;
	public static final byte VIBRASLAP = 58;
	public static final byte RIDE_CYMBAL_2 = 59;

	public static final byte HI_BONGO = 60;
	public static final byte LOW_BONGO = 61;
	public static final byte MUTE_HI_CONGA = 62;
	public static final byte OPEN_HI_CONGA = 63;
	public static final byte LOW_CONGA = 64;
	public static final byte HIGH_TIMBALE = 65;
	public static final byte LOW_TIMBALE = 66;
	public static final byte HIGH_AGOGO = 67;
	public static final byte LOW_AGOGO = 68;
	public static final byte CABASA = 69;

	public static final byte MARACAS = 70;
	public static final byte SHORT_WHISTLE = 71;
	public static final byte LONG_WHISTLE = 72;
	public static final byte SHORT_GUIRO = 73;
	public static final byte LONG_GUIRO = 74;
	public static final byte CLAVES = 75;
	public static final byte HI_WOOD_BLOCK = 76;
	public static final byte LOW_WOOD_BLOCK = 77;
	public static final byte MUTE_CUICA = 78;
	public static final byte OPEN_CUICA = 79;

	public static final byte MUTE_TRIANGLE = 80;
	public static final byte OPEN_TRIANGLE = 81;

}

/*
 * 
 * Here's a handy chart from http://www.borg.com/~jglatt/tutr/notefreq.htm
 * 
 * MIDI MIDI MIDI Note Frequency Note Frequency Note Frequency C 0 8.1757989156
 * 12 16.3515978313 24 32.7031956626 Db 1 8.6619572180 13 17.3239144361 25
 * 34.6478288721 D 2 9.1770239974 14 18.3540479948 26 36.7080959897 Eb 3
 * 9.7227182413 15 19.4454364826 27 38.8908729653 E 4 10.3008611535 16
 * 20.6017223071 28 41.2034446141 F 5 10.9133822323 17 21.8267644646 29
 * 43.6535289291 Gb 6 11.5623257097 18 23.1246514195 30 46.2493028390 G 7
 * 12.2498573744 19 24.4997147489 31 48.9994294977 Ab 8 12.9782717994 20
 * 25.9565435987 32 51.9130871975 A 9 13.7500000000 21 27.5000000000 33
 * 55.0000000000 Bb 10 14.5676175474 22 29.1352350949 34 58.2704701898 B 11
 * 15.4338531643 23 30.8677063285 35 61.7354126570
 * 
 * C 36 65.4063913251 48 130.8127826503 60 261.6255653006 Db 37 69.2956577442 49
 * 138.5913154884 61 277.1826309769 D 38 73.4161919794 50 146.8323839587 62
 * 293.6647679174 Eb 39 77.7817459305 51 155.5634918610 63 311.1269837221 E 40
 * 82.4068892282 52 164.8137784564 64 329.6275569129 F 41 87.3070578583 53
 * 174.6141157165 65 349.2282314330 Gb 42 92.4986056779 54 184.9972113558 66
 * 369.9944227116 G 43 97.9988589954 55 195.9977179909 67 391.9954359817 Ab 44
 * 103.8261743950 56 207.6523487900 68 415.3046975799 A 45 110.0000000000 57
 * 220.0000000000 69 440.0000000000 Bb 46 116.5409403795 58 233.0818807590 70
 * 466.1637615181 B 47 123.4708253140 59 246.9416506281 71 493.8833012561
 * 
 * C 72 523.2511306012 84 1046.5022612024 96 2093.0045224048 Db 73
 * 554.3652619537 85 1108.7305239075 97 2217.4610478150 D 74 587.3295358348 86
 * 1174.6590716696 98 2349.3181433393 Eb 75 622.2539674442 87 1244.5079348883 99
 * 2489.0158697766 E 76 659.2551138257 88 1318.5102276515 100 2637.0204553030 F
 * 77 698.4564628660 89 1396.9129257320 101 2793.8258514640 Gb 78 739.9888454233
 * 90 1479.9776908465 102 2959.9553816931 G 79 783.9908719635 91 1567.9817439270
 * 103 3135.9634878540 Ab 80 830.6093951599 92 1661.2187903198 104
 * 3322.4375806396 A 81 880.0000000000 93 1760.0000000000 105 3520.0000000000 Bb
 * 82 932.3275230362 94 1864.6550460724 106 3729.3100921447 B 83 987.7666025122
 * 95 1975.5332050245 107 3951.0664100490
 * 
 * C 108 4186.0090448096 120 8372.0180896192 Db 109 4434.9220956300 121
 * 8869.8441912599 D 110 4698.6362866785 122 9397.2725733570 Eb 111
 * 4978.0317395533 123 9956.0634791066 E 112 5274.0409106059 124
 * 10548.0818212118 F 113 5587.6517029281 125 11175.3034058561 Gb 114
 * 5919.9107633862 126 11839.8215267723 G 115 6271.9269757080 127
 * 12543.8539514160 Ab 116 6644.8751612791 A 117 7040.0000000000 Bb 118
 * 7458.6201842894 B 119 7902.1328200980
 * 
 * Note: Middle C is note #60. Frequency is in Hertz.
 * 
 * 
 */